java Unsafe操作主内存

前言

Unsafe可以用于获取成员字段在类实例中的偏移量,直接操作变量在主内存中的值等操作,是实现原子类的关键。

jdk示例

unsafe.objectFieldOffset(Field f)方法,获取成员变量地址偏移量

以jdk AtomicInteger类的具体实现为例,进行说明:

public class AtomicInteger extends Number implements java.io.Serializable {

private static final longserialVersionUID = 6214790243416807050L;

// 获取Unsafe实例
private static final Unsafe unsafe = Unsafe.getUnsafe();

private volatile int value;

//value字段相对AtomicInteger对象内存地址的偏移量
private static final longvalueOffset;

static {
try {
//类初始化时,计算value字段相对AtomicInteger对象内存地址的偏移量量
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { thrownew Error(ex); }
}

}

unsafe.compareAndSwapInt 比较并修改主内存中对象字段的值

以jdk AtomicBoolean类的具体实现为例,进行说明:

//boolean类型在jvm中,使用int类型来表达的,占4字节
public class AtomicBoolean implements java.io.Serializable {

public final boolean get() {
return value != 0;
}

public final boolean compareAndSet(boolean expect, boolean update) {
int e = expect ? 1 : 0;
int u = update ? 1 : 0;
//this:修改当前对象,字段偏移量为valueOffset的值,其中expect为主内存中期望值,update为待更新值
//如果与期望的值相等,则更新主内存该对象中字段值为update,并返回true
//更新失败,则返回false
return unsafe.compareAndSwapInt(this, valueOffset, e, u);
}

}

根据字段偏移量获取实例中此字段的值

@Test
public void unsafeTest() {
long offset = unsafe.objectFieldOffset(User.class.getDeclaredField("age"));
//获取int型值:getInt(),getObject(),getLong()等
Object obj = unsafe.getInt(this, offset);
System.out.println(obj);
}

获取Unsafe实例

/**import sun.misc.Unsafe;导入此包 
* 反射获取该实例,还绕开了安全管理器的限制
*/
private static Unsafe getUnsafeInstance() throws SecurityException,
NoSuchFieldException, IllegalArgumentException, IllegalAccessException
{
Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeInstance.setAccessible(true);

//返回指定对象上此Field字段表示的值
return (Unsafe) theUnsafeInstance.get(Unsafe.class);
}

总结

Unsafe类提供了直接操作主内存(堆内存)的方法,但sun公司是通过安全管理器限制了直接获取Unsafe的,毕竟应用层直接操作主内存是不安全的,当然也是不允许的,由于篇幅有限,以上只是介绍了Unsafe类中比较常用的方法。